package checkers3d.presentation;

import java.awt.Point;
import java.util.LinkedList;

/**
 * A container object that holds GUI IDrawables objects. Allows for components
 * to be rendered relative to this objects position. Funnels events to contained
 * objects.
 *
 * @author      Ruben Acuna
 */
public class GUIContainer implements IInputObserver
{
    /**
    * List of IDrawable components that this GUIContainer manages.
    */
    private LinkedList<IDrawable> components;

    /**
    * Position of the GUIContainer in screen space.
    */
    private Point position;

    /**
     * The dimensions of the GUIContainer in pixels.
     */
    Point size;

    LinkedList<ParallaxBackground> parallaxBackgrounds;

    /**
    * Class constructor that creates a GUIContainer at (0,0) with no components.
    */
    public GUIContainer(Point size)
    {
        this(new Point(0,0), size);
    }

    /**
    * Class constructor that creates a GUIContainer at a given point with no
    * components components.
    */
    public GUIContainer(Point positon, Point size)
    {
        components = new LinkedList<IDrawable>();
        parallaxBackgrounds = new LinkedList<ParallaxBackground>();
        setDrawPosition(position);

        this.size = size;
    }

    /**
    * Adds a component (IDrawable) the GUIContainer.
    *
    * @param  drawable Component (IDrawable) to add.
    */
    public void add(IDrawable drawable)
    {
        components.add(drawable);
    }

    /**
    * Handles a click by sending it to all of its components.

    *
    * @param  position  Point object containing the relative screen coordinates
    *                   of the click.
    */
    public void onClick(Point position)
    {
        //TODO: Here we handle selecting the component in focus, right now only
        //      textboxes use focus, so we only check for them. In the future we
        //      should move focus into an interface.

        //remove focus from components.
        for(IDrawable drawable : components)
        {
            if(drawable instanceof GUITextbox)
                ((GUITextbox)drawable).setFocus(false);
        }

        for(IDrawable drawable : components)
        {
            if(drawable instanceof IInputObserver )
            {
                //Translate the point from screen space to GUIContainer space.
                Point xy = new Point(position.x - drawable.getDrawPosition().x,
                                     position.y - drawable.getDrawPosition().y);

                if(drawable instanceof GUITextbox)
                {
                    if(drawable.getRenderResource().containsPoint(xy))
                        ((GUITextbox)drawable).setFocus(true);
                }

                ((IInputObserver)drawable).onClick(xy);
            }
        }
    }

    /**
    * Handles a key press, when the GUIContainer is in focus, by sending it to
    * all of its components.
    *
    * @param  key       The character of the key that was pressed.
    * @see IInputObserver
    */
    public void onKeyPress(char key)
    {
        for(IDrawable observer : components)
        {
            if(observer instanceof IInputObserver)
            {
                ((IInputObserver)observer).onKeyPress(key);
            }
        }
    }

    /**
    * Returns a reference to the list of components in this GUIContainer.
    *
    * @return List of components (IDrawable's) in this GUIContainer.
    */    
    public LinkedList<IDrawable> getComponentsList()
    {
        return components;
    }

    /**
    * Returns the position of the GUILabel.
    *
    * @return The position of this GUILabel.
    */
    public Point getDrawPosition()
    {
        return position;
    }

    /**
    * Sets the current the position of the GUILabel.
    *
    * @param  position The new position of this GUILabel.
    */
    public void setDrawPosition(Point position)
    {
        this.position = position;
    }

    /**
     * Returns the dimensions of this GUIContainer in pixels.
     *
     * @return Point whose components are the dimensions of this GUIContainer.
     */
    public Point getSize()
    {
        return size;
    }

    /**
     * Informs the GUIContainer that a given amount of time in milliseconds has
     * elapsed. Used for animation and time events.
     *
     * @param ms Milliseconds elapsed.
     */
    public void tick(int ms)
    {
        for(ParallaxBackground bg : parallaxBackgrounds)
        {
            bg.tick(ms);
        }
    }

    /**
     * Adds a background to this GUIContainer that does not move.
     *
     * @param background Reference to the IDrawable background.
     */
    public void addBackground(IDrawable background)
    {
        addBackground(background, new Vector());
    }

    /**
     * Adds a background to this GUIContainer that moves according to a vector.
     *
     * @param background Reference to the IDrawable background.
     * @param translator Vector of movement in pixels per second.
     */
    public void addBackground(IDrawable background, Vector translator)
    {
        parallaxBackgrounds.add(new ParallaxBackground(background, translator.getScaledCopy(1 / 1000f), this));
    }

    /**
     * Returns true if there are active backgrounds in this GUIContainer.
     *
     * @return True if there are active backgrounds.
     */
    public boolean hasParallaxBackgrounds()
    {
        if(parallaxBackgrounds.size() > 0 )
            return true;
        else
            return false;
    }

    /**
     * Class representing a parallax background capable of scrolling.
     */
    private class ParallaxBackground
    {
        /**
         * IDrawable for the right most copy of the background.
         */
        private IDrawable drawableBottomRight;

        /**
         * IDrawable for the left most copy of the background.
         */
        private IDrawable drawableBottomLeft;

        /**
         * A vector with the pixels per millisecond the background should move.
         */
        private Vector globalTranslator;

        /**
         * Position of the right most copy of the background.
         */
        private Vector positionRight;
        
        /**
         * Position of the left most copy of the background.
         */
        private Vector positionLeft;

        /**
         * Class constructor that takes in a IDrawable to scroll, a vector for
         * it's movement and the GUIContainer it exists within.
         *
         * @param drawable IDrawable to scroll
         * @param translator A vector with the pixels per millisecond the background should move.
         * @param parent GUIContainer with this background.
         */
        public ParallaxBackground(IDrawable drawable, Vector translator, GUIContainer parent)
        {
            drawableBottomRight = drawable;
            drawableBottomLeft = new GUIPrimitive(drawable.getRenderResource().clone(), new Point ());

            globalTranslator = translator;

            positionRight = new Vector(drawable.getDrawPosition().x, drawable.getDrawPosition().y);
            positionLeft = new Vector(-drawable.getRenderResource().getWidth(), drawable.getDrawPosition().y);

            add(drawableBottomRight);
            add(drawableBottomLeft);
        }

        /**
         * Informs the ParallaxBackground that a given amount of time in milliseconds has
         * elapsed. Used for scrolling.
         *
         * @param ms Milliseconds elapsed.
         */
        public void tick(int ms)
        {
            positionRight.translate(globalTranslator.getScaledCopy(ms));
            positionLeft.translate(globalTranslator.getScaledCopy(ms));

            drawableBottomRight.getDrawPosition().setLocation(positionRight.x, positionRight.y);
            drawableBottomLeft.getDrawPosition().setLocation(positionLeft.x, positionLeft.y);

            //screen moved to the left.
            if(positionRight.x > GameEngine.WINDOW_SIZE.x)
            {
                positionRight.x = positionLeft.x - drawableBottomRight.getRenderResource().getWidth();

                Vector temp = positionRight.clone();
                positionRight = positionLeft.clone();
                positionLeft = temp;
            }
            //screen moved to the right.
            else if(positionLeft.x + drawableBottomRight.getRenderResource().getWidth() < 0)
            {
                positionLeft.x = positionRight.x + drawableBottomLeft.getRenderResource().getWidth();

                Vector temp = positionLeft.clone();
                positionLeft = positionRight.clone();
                positionRight = temp;
            }
        }
    }

    /**
    * Returns a string representation of the GUIContainer which contains
    * information about its position and the components it contains.
    *
    * @return       A string representation of this GUIContainer.
    */
    @Override
    public String toString()
    {
        String text = getClass().getName() + " -> " + position.toString()
                                                    + ", [";

        for(IDrawable observer : components)
        {
            text += observer.toString();
            text += ", ";
        }

        text += "]";

        return text;
    }
}